Obvladajte TypeScriptove deklaracijske datoteke (.d.ts) za varnost tipov in samodejno dokončevanje za katero koli knjižnico JavaScript. Naučite se uporabljati @types, ustvarjati lastne definicije in obravnavati kodo tretjih oseb kot profesionalec.
Odklepanje ekosistema JavaScript: Poglobljen vpogled v TypeScriptove deklaracijske datoteke
TypeScript je revolucioniral sodoben spletni razvoj z uvedbo statičnega tipkanja v dinamični svet JavaScripta. Ta varnost tipov prinaša neverjetne prednosti: odkrivanje napak med prevajanjem, omogočanje zmogljivega samodejnega dokončevanja v urejevalniku in znatno izboljšanje vzdrževanja velikih zbirk kode. Vendar pa se pojavi velik izziv, ko želimo uporabiti obsežen ekosistem obstoječih knjižnic JavaScript – od katerih večina ni bila napisana v TypeScriptu. Kako naša strogo tipkana koda TypeScript razume oblike, funkcije in spremenljivke iz netipkane knjižnice JavaScript?
Odgovor se skriva v TypeScriptovih deklaracijskih datotekah. Te datoteke, ki jih je mogoče prepoznati po priponi .d.ts, so bistven most med svetovoma TypeScript in JavaScript. Delujejo kot načrt ali pogodba API, ki opisuje tipe knjižnice tretje osebe, ne da bi vsebovala njeno dejansko implementacijo. V tem celovitem vodniku bomo raziskali vse, kar morate vedeti, da boste samozavestno upravljali definicije tipov za katero koli knjižnico JavaScript v svojih projektih TypeScript.
Kaj natančno so TypeScriptove deklaracijske datoteke?
Predstavljajte si, da ste najeli izvajalca, ki govori samo drug jezik. Za učinkovito delo z njimi bi potrebovali prevajalca ali podroben nabor navodil v jeziku, ki ga oba razumeta. Deklaracijska datoteka služi točno temu namenu za prevajalnik TypeScript (izvajalec).
Datoteka .d.ts vsebuje samo informacije o tipih. Vključuje:
- Podpise za funkcije in metode (tipi parametrov, tipi povratnih vrednosti).
- Definicije za spremenljivke in njihove tipe.
- Vmesnike in psevdonime tipov za kompleksne objekte.
- Definicije razredov, vključno z njihovimi lastnostmi in metodami.
- Strukture imenskih prostorov in modulov.
Ključno je, da te datoteke ne vsebujejo izvedljive kode. Namenjene so izključno statični analizi. Ko uvozite knjižnico JavaScript, kot je Lodash, v svoj projekt TypeScript, prevajalnik poišče ustrezno deklaracijsko datoteko. Če jo najde, lahko preveri vašo kodo, zagotovi inteligentno samodejno dokončevanje in zagotovi, da knjižnico uporabljate pravilno. Če je ne najde, bo sprožil napako, kot je: Could not find a declaration file for module 'lodash'.
Zakaj so deklaracijske datoteke nujne za profesionalni razvoj
Uporaba knjižnic JavaScript brez ustreznih definicij tipov v projektu TypeScript spodkopava sam razlog za uporabo TypeScripta. Oglejmo si preprost scenarij z uporabo priljubljene pripomočkovne knjižnice Lodash.
Svet brez definicij tipov
Brez deklaracijske datoteke TypeScript nima pojma, kaj je lodash ali kaj vsebuje. Da bi sploh dosegli, da se koda prevede, boste morda v skušnjavi uporabiti hitro rešitev, kot je ta:
const _: any = require('lodash');
const users = [{ 'user': 'barney' }, { 'user': 'fred' }];
// Autocomplete? No help here.
// Type checking? No. Is 'username' the correct property?
// The compiler allows this, but it might fail at runtime.
_.find(users, { username: 'fred' });
V tem primeru je spremenljivka _ tipa any. To učinkovito pove TypeScriptu: "Ne preverjaj ničesar, kar je povezano s to spremenljivko." Izgubite vse prednosti: brez samodejnega dokončevanja, brez preverjanja tipov na argumentih in brez gotovosti glede povratnega tipa. To je plodno okolje za napake pri izvajanju.
Svet z definicijami tipov
Zdaj pa poglejmo, kaj se zgodi, ko zagotovimo potrebno deklaracijsko datoteko. Po namestitvi tipov (kar bomo obravnavali naslednjič) se izkušnja spremeni:
import _ from 'lodash';
interface User {
user: string;
active?: boolean;
}
const users: User[] = [{ 'user': 'barney' }, { 'user': 'fred' }];
// 1. Editor provides autocompletion for 'find' and other lodash functions.
// 2. Hovering over 'find' shows its full signature and documentation.
// 3. TypeScript sees that `users` is an array of `User` objects.
// 4. TypeScript knows the predicate for `find` on `User[]` should involve `user` or `active`.
// CORRECT: TypeScript is happy.
const fred = _.find(users, { user: 'fred' });
// ERROR: TypeScript catches the mistake!
// Property 'username' does not exist on type 'User'.
const betty = _.find(users, { username: 'betty' });
Razlika je očitna. Pridobimo popolno varnost tipov, vrhunsko razvijalsko izkušnjo prek orodij in dramatično zmanjšanje potencialnih napak. To je profesionalni standard za delo s TypeScriptom.
Hierarhija iskanja definicij tipov
Kako torej dobite te čarobne datoteke .d.ts za svoje najljubše knjižnice? Obstaja uveljavljen postopek, ki zajema veliko večino scenarijev.
1. korak: Preverite, ali knjižnica vsebuje lastne tipe
Najboljši scenarij je, ko je knjižnica napisana v TypeScriptu ali njeni vzdrževalci zagotavljajo uradne deklaracijske datoteke znotraj istega paketa. To postaja vse bolj pogosto za sodobne projekte, ki se dobro vzdržujejo.
Kako preveriti:
- Namestite knjižnico kot običajno:
npm install axios - Poglejte v mapo knjižnice v
node_modules/axios. Ali vidite kakršne koli datoteke.d.ts? - Preverite datoteko
package.jsonknjižnice za polje"types"ali"typings". To polje kaže neposredno na glavno deklaracijsko datoteko. Na primer, Axiosova datotekapackage.jsonvsebuje:"types": "index.d.ts".
Če so ti pogoji izpolnjeni, ste končali! TypeScript bo samodejno našel in uporabil te vključene tipe. Nadaljnje ukrepanje ni potrebno.
2. korak: Projekt DefinitelyTyped (@types)
Za tisoče knjižnic JavaScript, ki ne vključujejo lastnih tipov, je globalna skupnost TypeScript ustvarila neverjeten vir: DefinitelyTyped.
DefinitelyTyped je centralizirano repozitorij, ki ga upravlja skupnost na GitHubu in gosti visokokakovostne deklaracijske datoteke za ogromno število paketov JavaScript. Te definicije so objavljene v registru npm pod obsegom @types.
Kako ga uporabljati:
Če knjižnica, kot je lodash, ne vključuje lastnih tipov, preprosto namestite ustrezen paket @types kot razvojno odvisnost:
npm install --save-dev @types/lodash
Imenovalna konvencija je preprosta in predvidljiva: za paket z imenom package-name bodo njegovi tipi skoraj vedno na @types/package-name. Razpoložljive tipe lahko poiščete na spletnem mestu npm ali neposredno v repozitoriju DefinitelyTyped.
Zakaj --save-dev? Deklaracijske datoteke so potrebne samo med razvojem in prevajanjem. Ne vsebujejo nobene izvajalne kode, zato jih ne bi smeli vključiti v končni produkcijski paket. Namestitev kot devDependency zagotavlja to ločitev.
3. korak: Ko tipi ne obstajajo - pisanje lastnih
Kaj pa, če uporabljate starejšo, nišno ali interno zasebno knjižnico, ki ne vključuje tipov in ni na DefinitelyTyped? V tem primeru si morate zavihiti rokave in ustvariti svojo deklaracijsko datoteko. Čeprav se to morda sliši zastrašujoče, lahko začnete preprosto in dodate več podrobnosti, kot je potrebno.
Hitra rešitev: Okrajšana ambientna deklaracija modula
Včasih morate samo poskrbeti, da se vaš projekt prevede brez napak, medtem ko si prizadevate za ustrezno strategijo tipkanja. Ustvarite lahko datoteko v svojem projektu (npr. declarations.d.ts ali types/global.d.ts) in dodate okrajšano deklaracijo:
// in a .d.ts file
declare module 'some-untyped-library';
To pove TypeScriptu: "Zaupajte mi, modul z imenom 'some-untyped-library' obstaja. Samo obravnavajte vse, kar je uvoženo iz njega, kot tip any." To utiša napako prevajalnika, vendar, kot smo že omenili, žrtvuje vso varnost tipov za to knjižnico. To je začasni popravek, ne dolgoročna rešitev.
Ustvarjanje osnovne deklaracijske datoteke po meri
Boljši pristop je, da začnete določati tipe za dele knjižnice, ki jih dejansko uporabljate. Recimo, da imamo preprosto knjižnico, imenovano `string-utils`, ki izvozi eno samo funkcijo.
// In node_modules/string-utils/index.js
module.exports.capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);
Ustvarimo lahko datoteko string-utils.d.ts v namenski mapi `types` v korenu našega projekta.
// In my-project/types/string-utils.d.ts
declare module 'string-utils' {
export function capitalize(str: string): string;
// You could add other function definitions here as you use them
// export function slugify(str: string): string;
}
Zdaj moramo povedati TypeScriptu, kje naj najde naše definicije tipov po meri. To storimo v tsconfig.json:
{
"compilerOptions": {
// ... other options
"baseUrl": ".",
"paths": {
"*": ["types/*"]
}
}
}
S to nastavitvijo bo TypeScript ob import { capitalize } from 'string-utils' našel vašo deklaracijsko datoteko po meri in zagotovil varnost tipov, ki ste jo definirali. To datoteko lahko postopoma gradite, ko uporabljate več funkcij knjižnice.
Poglabljanje: Pisanje deklaracijskih datotek
Raziščimo nekaj naprednejših konceptov, s katerimi se boste srečali pri pisanju ali branju deklaracijskih datotek.
Deklariranje različnih vrst izvozov
Moduli JavaScript lahko izvažajo stvari na različne načine. Vaša deklaracijska datoteka se mora ujemati s strukturo izvoza knjižnice.
- Poimenovani izvozi: To je najpogostejše. Videli smo ga zgoraj z `export function capitalize(...)`. Izvažate lahko tudi konstante, vmesnike in razrede.
- Privzeti izvoz: Za knjižnice, ki uporabljajo `export default`.
- UMD Globals: Za starejše knjižnice, zasnovane za delo v brskalnikih prek oznake
<script>, se pogosto pripnejo globalnemu objektu `window`. Lahko deklarirate te globalne spremenljivke. - `export =` in `import = require()`: Ta sintaksa je za starejše module CommonJS, ki uporabljajo `module.exports = ...`. Na primer, če knjižnica naredi `module.exports = myClass;`.
declare module 'my-lib' {
export const version: string;
export interface Options { retries: number; }
export function doSomething(options: Options): Promise
declare module 'my-default-lib' {
// For a function default export
export default function myCoolFunction(): void;
// For an object default export
// const myLib = { name: 'lib', version: '1.0' };
// export default myLib;
}
// Declares a global variable '$' of a certain type
declare var $: JQueryStatic;
// in my-class.d.ts
declare class MyClass { constructor(name: string); }
export = MyClass;
// in your app.ts
import MyClass = require('my-class');
const instance = new MyClass('test');
Čeprav je manj pogosta pri sodobnih modulih ES, je ključna za združljivost z mnogimi starejšimi, a še vedno široko uporabljanimi paketi Node.js.
Razširitev modula: Razširitev obstoječih tipov
Ena najmočnejših funkcij je razširitev modula (znana tudi kot spajanje deklaracij). To vam omogoča, da dodate lastnosti obstoječim vmesnikom, definiranim v deklaracijski datoteki drugega paketa. To je izjemno uporabno za knjižnice z arhitekturo vtičnikov, kot sta Express ali Fastify.
Predstavljajte si, da uporabljate vmesno programsko opremo v Expressu, ki doda lastnost `user` objektu `Request`. Brez razširitve bi se TypeScript pritožil, da `user` ne obstaja v `Request`.
Takole lahko TypeScriptu poveste o tej novi lastnosti:
// in your types/express.d.ts file
// We must import the original type to augment it
import { UserProfile } from './auth'; // Assuming you have a UserProfile type
// Tell TypeScript we're augmenting the 'express-serve-static-core' module
declare module 'express-serve-static-core' {
// Target the 'Request' interface inside that module
interface Request {
// Add our custom property
user?: UserProfile;
}
}
Zdaj bo v celotni vaši aplikaciji objekt Express `Request` pravilno tipkan z neobvezno lastnostjo `user`, in dobili boste popolno varnost tipov in samodejno dokončevanje.
Direktive s tremi poševnicami
Včasih boste na vrhu datotek .d.ts videli komentarje, ki se začnejo s tremi poševnicami (///). To so direktive s tremi poševnicami, ki delujejo kot navodila prevajalnika.
/// <reference types="..." />: To je najpogostejša. Izrecno vključuje definicije tipov drugega paketa kot odvisnost. Na primer, tipi za vtičnik WebdriverIO lahko vključujejo/// <reference types="webdriverio" />, ker so njegovi tipi odvisni od osnovnih tipov WebdriverIO./// <reference path="..." />: To se uporablja za deklariranje odvisnosti od druge datoteke znotraj istega projekta. To je starejša sintaksa, ki jo v veliki meri nadomeščajo uvozi modulov ES.
Najboljše prakse za upravljanje deklaracijskih datotek
- Dajte prednost vključenim tipom: Pri izbiri med knjižnicami dajte prednost tistim, ki so napisane v TypeScriptu ali vključujejo lastne uradne definicije tipov. To signalizira zavezanost ekosistemu TypeScript.
- Hranite
@typesvdevDependencies: Vedno namestite pakete@typesz--save-devali-D. Niso potrebni za vašo produkcijsko kodo. - Uskladite različice: Pogost vir napak je neskladje med različico knjižnice in njeno različico
@types. Večja posodobitev različice v knjižnici (npr. z v2 na v3) bo verjetno imela prelomne spremembe v njenem API-ju, kar se mora odražati v paketu@types. Poskusite jih ohraniti sinhronizirane. - Uporabite
tsconfig.jsonza nadzor: Možnosti prevajalnikatypeRootsintypesv vaši datotekitsconfig.jsonvam lahko omogočijo natančen nadzor nad tem, kje TypeScript išče deklaracijske datoteke.typeRootspove prevajalniku, katere mape naj preveri (privzeto je to./node_modules/@types), intypesvam omogoča, da izrecno navedete, katere pakete tipov naj vključite. - Prispevajte nazaj: Če napišete celovito deklaracijsko datoteko za knjižnico, ki je nima, razmislite o tem, da jo prispevate k projektu DefinitelyTyped. To je fantastičen način, da vrnete globalni razvijalski skupnosti in pomagate na tisoče drugih.
Sklep: Nepozabljeni junaki varnosti tipov
TypeScriptove deklaracijske datoteke so neopevani junaki, ki omogočajo brezhibno integracijo dinamičnega in razširjenega sveta JavaScripta v robustno in varno razvojno okolje. So kritična povezava, ki opolnomoči naša orodja, preprečuje nešteto napak in naredi naše zbirke kode bolj odporne in samodejno dokumentirane.
Z razumevanjem, kako najti, uporabiti in celo ustvariti lastne datoteke .d.ts, ne popravljate samo napake prevajalnika – dvigujete celoten potek dela razvoja. Odklepate ves potencial TypeScripta in bogatega ekosistema knjižnic JavaScript, kar ustvarja močno sinergijo, ki vodi do boljše in zanesljivejše programske opreme za globalno občinstvo.